Celem niniejszego raportu jest przeprowadzenie analizy danych dotyczących rynku nieruchomości. Wykorzystano w tym celu szereg technik eksploracyjnych oraz metod przetwarzania danych, takich jak analiza brakujących wartości, imputacja danych, identyfikacja obserwacji odstających oraz wizualizacja wyników.
Raport jest podzielony na kilka kluczowych sekcji, każda z nich koncentruje się na innym aspekcie analizy. Na początku zidentyfikowane zostaną brakujące dane, a następnie zostaną przeprowadzone transformacje mające na celu poprawę jakości danych. Kolejne sekcje poświęcone są analizie statystycznej oraz wizualizacji danych, co pozwoli lepiej zrozumieć zależności panujące na rynku nieruchomości.
agencja_nieruchomosci <- read.csv("agencja_nieruchomosci.csv")
knitr::kable(head(agencja_nieruchomosci, 10)) %>%
kable_styling(font_size = 10)| price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | furnishingstatus |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| NA | 7420 | 4 | 2 | 3 | NA | no | no | no | yes | 2 | yes | furnished |
| 12250000 | 8960 | 4 | 4 | 4 | yes | no | no | no | yes | 3 | no | furnished |
| 12250000 | 9960 | 3 | 2 | 2 | yes | no | yes | no | no | 2 | yes | semi-furnished |
| 12215000 | 7500 | 4 | 2 | 2 | yes | no | yes | no | yes | 3 | yes | furnished |
| 11410000 | 7420 | 4 | 1 | 2 | yes | yes | yes | no | yes | 2 | no | furnished |
| 10850000 | 7500 | 3 | 3 | 1 | yes | no | yes | no | yes | 2 | yes | semi-furnished |
| 10150000 | 8580 | 4 | 3 | 4 | yes | no | no | no | yes | 2 | yes | semi-furnished |
| 10150000 | 16200 | 5 | 3 | 2 | yes | no | no | no | no | 0 | no | unfurnished |
| 9870000 | 8100 | 4 | 1 | 2 | yes | yes | yes | no | yes | 2 | yes | furnished |
| 9800000 | 5750 | 3 | 2 | 4 | yes | yes | no | no | yes | 1 | yes | unfurnished |
Pierwszym etapem analizy jest identyfikacja brakujących wartości w zbiorze danych. Braki w danych mogą wynikać z wielu czynników, takich jak błędy w zbieraniu informacji czy celowe pominięcie pewnych elementów. W tej sekcji dokonano identyfikacji oraz przedstawiono sposoby radzenia sobie z brakującymi danymi.
Analiza wykazała, że najwięcej brakujących wartości występuje w kolumnie cena. Braki te mogą wynikać z strategii sprzedających, którzy decydują się nie podawać cen, aby zebrać oferty od potencjalnych kupców.
Ogólne podsumowanie brakujących wartości
| variable | n_miss | pct_miss |
|---|---|---|
| price | 110 | 20.2 |
| mainroad | 50 | 9.17 |
| prefarea | 50 | 9.17 |
| area | 0 | 0 |
| bedrooms | 0 | 0 |
| bathrooms | 0 | 0 |
| stories | 0 | 0 |
| guestroom | 0 | 0 |
| basement | 0 | 0 |
| hotwaterheating | 0 | 0 |
| airconditioning | 0 | 0 |
| parking | 0 | 0 |
| furnishingstatus | 0 | 0 |
Wprowadzając podstawowe reguły, na przykład weryfikując czy cena nie
jest ujemna, mozemy zweryfikować sensowność naszych danych. Po
określeniu reguł są one upraszczane. Funkcja simplify_rules
pozwala sprawdzić czy nie ma w nich sprzeczności i wyeliminować
duplikaty. W tym przypadku wyczyszczenie reguł nie przyniosło zadnych
efektów.
rules <- validator(
price > 0 & price < 100000000,
area > 0 & area < 100000,
bedrooms > -1 & bedrooms < 10,
bathrooms > -1 & bathrooms < 10,
stories > -1 & stories < 20,
parking > -1 & parking < 5,
mainroad %in% c("yes", "no"),
guestroom %in% c("yes", "no"),
basement %in% c("yes", "no"),
hotwaterheating %in% c("yes", "no"),
airconditioning %in% c("yes", "no"),
prefarea %in% c("yes", "no"),
furnishingstatus %in% c(
"furnished",
"semi-furnished",
"unfurnished"
)
)
warnings()
validation_results <- confront(agencja_nieruchomosci, rules)
knitr::kable(summary(validation_results))| name | items | passes | fails | nNA | error | warning | expression |
|---|---|---|---|---|---|---|---|
| V01 | 545 | 435 | 0 | 110 | FALSE | FALSE | price > 0 & price < 1e+08 |
| V02 | 545 | 545 | 0 | 0 | FALSE | FALSE | area > 0 & area < 1e+05 |
| V03 | 545 | 545 | 0 | 0 | FALSE | FALSE | bedrooms > -1 & bedrooms < 10 |
| V04 | 545 | 545 | 0 | 0 | FALSE | FALSE | bathrooms > -1 & bathrooms < 10 |
| V05 | 545 | 545 | 0 | 0 | FALSE | FALSE | stories > -1 & stories < 20 |
| V06 | 545 | 545 | 0 | 0 | FALSE | FALSE | parking > -1 & parking < 5 |
| V07 | 545 | 495 | 0 | 50 | FALSE | FALSE | mainroad %vin% c(“yes”, “no”) |
| V08 | 545 | 545 | 0 | 0 | FALSE | FALSE | guestroom %vin% c(“yes”, “no”) |
| V09 | 545 | 545 | 0 | 0 | FALSE | FALSE | basement %vin% c(“yes”, “no”) |
| V10 | 545 | 545 | 0 | 0 | FALSE | FALSE | hotwaterheating %vin% c(“yes”, “no”) |
| V11 | 545 | 545 | 0 | 0 | FALSE | FALSE | airconditioning %vin% c(“yes”, “no”) |
| V12 | 545 | 495 | 0 | 50 | FALSE | FALSE | prefarea %vin% c(“yes”, “no”) |
| V13 | 545 | 545 | 0 | 0 | FALSE | FALSE | furnishingstatus %vin% c(“furnished”, “semi-furnished”, “unfurnished”) |
Funkcje summary i barplot zgodnie pokazują
ze wszystkie nasze dane mają realne wartości.
Aby ułatwić dalszą pracę można zmienić wartości binarne yes i no na 0 i 1. Wartościom z kolumny furnished nadajemy kolejno wartości 0, 1 i 2 (od unfurnished do furnished).
agencja_nieruchomosci$mainroad <-
ifelse(agencja_nieruchomosci$mainroad == "yes", 1, 0)
agencja_nieruchomosci$guestroom <-
ifelse(agencja_nieruchomosci$guestroom == "yes", 1, 0)
agencja_nieruchomosci$basement <-
ifelse(agencja_nieruchomosci$basement == "yes", 1, 0)
agencja_nieruchomosci$hotwaterheating <-
ifelse(agencja_nieruchomosci$hotwaterheating == "yes", 1, 0)
agencja_nieruchomosci$airconditioning <-
ifelse(agencja_nieruchomosci$airconditioning == "yes", 1, 0)
agencja_nieruchomosci$prefarea <-
ifelse(agencja_nieruchomosci$prefarea == "yes", 1, 0)
agencja_nieruchomosci$furnishingstatus <- ifelse(
agencja_nieruchomosci$furnishingstatus == "unfurnished",
0,
ifelse(
agencja_nieruchomosci$furnishingstatus == "semi-furnished",
1,
2
)
)
knitr::kable(head(agencja_nieruchomosci, 10)) %>%
kable_styling(font_size = 10)| price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | furnishingstatus |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| NA | 7420 | 4 | 2 | 3 | NA | 0 | 0 | 0 | 1 | 2 | 1 | 2 |
| 12250000 | 8960 | 4 | 4 | 4 | 1 | 0 | 0 | 0 | 1 | 3 | 0 | 2 |
| 12250000 | 9960 | 3 | 2 | 2 | 1 | 0 | 1 | 0 | 0 | 2 | 1 | 1 |
| 12215000 | 7500 | 4 | 2 | 2 | 1 | 0 | 1 | 0 | 1 | 3 | 1 | 2 |
| 11410000 | 7420 | 4 | 1 | 2 | 1 | 1 | 1 | 0 | 1 | 2 | 0 | 2 |
| 10850000 | 7500 | 3 | 3 | 1 | 1 | 0 | 1 | 0 | 1 | 2 | 1 | 1 |
| 10150000 | 8580 | 4 | 3 | 4 | 1 | 0 | 0 | 0 | 1 | 2 | 1 | 1 |
| 10150000 | 16200 | 5 | 3 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9870000 | 8100 | 4 | 1 | 2 | 1 | 1 | 1 | 0 | 1 | 2 | 1 | 2 |
| 9800000 | 5750 | 3 | 2 | 4 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 |
Brakujące wartości wg. stanu umeblowania
agencja_nieruchomosci %>%
group_by(furnishingstatus) %>%
miss_var_summary() %>%
filter(n_miss > 0) %>%
knitr::kable()| furnishingstatus | variable | n_miss | pct_miss |
|---|---|---|---|
| 2 | price | 33 | 23.6 |
| 2 | prefarea | 15 | 10.7 |
| 2 | mainroad | 11 | 7.86 |
| 1 | price | 43 | 18.9 |
| 1 | mainroad | 23 | 10.1 |
| 1 | prefarea | 18 | 7.93 |
| 0 | price | 34 | 19.1 |
| 0 | prefarea | 17 | 9.55 |
| 0 | mainroad | 16 | 8.99 |
| n_miss_in_case | n_cases | pct_cases |
|---|---|---|
| 0 | 363 | 66.6055046 |
| 1 | 155 | 28.4403670 |
| 2 | 26 | 4.7706422 |
| 3 | 1 | 0.1834862 |
W tabeli podsumowującej widzimy, że liczba pustych pól na obserwację waha się o. Spośród 545 obserwacji 363 jest kompletnych - to 66,6% obserwacji w danych. Tylko 1 obserwacja (0,18%) zawiera 3 brakujące wartości.
Dzięki wykresom, które przedstawiają procent brakujących danych w zależności od filtru można wywnioskować, że największy procent brakujących danych jest zazwyczaj w momencie, gdzie jest najwieksza wartość dodatnia z konkretnych filtrów. Przykładowo największa ilość NA (price) jest w momencie, gdzie są 3 miejsca parkingowe.
Na powyższym wykresie widać, że najwięcej brakujących danych jest w kolumnie cena. Przyczyny są prawdopodobnie czysto praktycznie - za wysoka cena może odstraszyć potencjalnych klientów. Potencjalną przyczyną może oznaczać chęć zbierania ofert.
ggplot(data = agencja_nieruchomosci, aes(x = area, y = price)) +
geom_point() +
geom_miss_point() +
scale_color_manual(values = c("darkorange", "cyan4")) +
theme_minimal()W pierwszym przypadku widzimy, że brak wartości price był podobnie prawdopodobny do większości przypadków powierzchni domu. Można zauważyć brak NA w wielkości domu od 12 tys. do 16 tys. jednak są tam tylko pojedyncze wartości.
ggplot(data = agencja_nieruchomosci, aes(x = area, y = price)) +
geom_point() +
geom_miss_point() +
scale_color_manual(values = c("darkorange", "cyan4")) +
theme_minimal() +
facet_wrap(~bedrooms)W podziale na podwykresy widzimy, że najwięcej NA znajduje sie w nieruchomościach, które mają 3 sypialnie.
W tej części raportu dokonano przekształcenia danych w taki sposób, aby ułatwić dalszą analizę. Zastosowano metody imputacji danych, takie jak k-NN oraz imputacja liniowa, aby uzupełnić brakujące wartości. Ponadto dokonano konwersji zmiennych kategorycznych do postaci numerycznej, co umożliwia ich dalsze wykorzystanie w modelach analitycznych.
Zidentyfikowano również obserwacje odstające w zmiennych liczbowych, które mogą wpływać na jakość analizy. Wykorzystano miary rozkładu oraz wykresy pudełkowe do wizualizacji i usunięcia skrajnych wartości, które mogłyby zaburzać wyniki.
Identyfikacja brakujących danych jest kluczowym elementem czyszczenia datasetu. Kolejnym krokiem jest imputacja, czyli uzupełnienie brakujących komórek. Jest wiele metod umożliwiających imputację. Jedną z najpopularniejszych metod jest algorytm k najbliższych sąsiadów (kNN). Poniżej wykorzystano tę metodę to uzupełnienia brakujących cen. Niezbędnym jest tutaj określenie ilości sąsiadów (k) branych pod uwagę przy regresji, arbitralnie wybrana została wartość k=15.
agencja_nieruchomosci <- kNN(
agencja_nieruchomosci,
variable = "price",
k = 15
)
agencja_nieruchomosci <- subset(agencja_nieruchomosci, select = -price_imp)
knitr::kable(miss_var_summary(agencja_nieruchomosci))| variable | n_miss | pct_miss |
|---|---|---|
| mainroad | 50 | 9.17 |
| prefarea | 50 | 9.17 |
| price | 0 | 0 |
| area | 0 | 0 |
| bedrooms | 0 | 0 |
| bathrooms | 0 | 0 |
| stories | 0 | 0 |
| guestroom | 0 | 0 |
| basement | 0 | 0 |
| hotwaterheating | 0 | 0 |
| airconditioning | 0 | 0 |
| parking | 0 | 0 |
| furnishingstatus | 0 | 0 |
Kolejną znaną metodą jest imputacja liniowa. Zmienne brakujące w
kolumnie mainroad uzupełnione zostały wykorzystując metodę
impute_lm pochodzącą z pakietu simputation.
Wartości te imputujemy na podstawie zmiennych price oraz parking.
agencja_nieruchomosci <- impute_lm(
agencja_nieruchomosci,
variable = "mainroad",
formula = mainroad ~ price + parking,
)
agencja_nieruchomosci$mainroad <- round(agencja_nieruchomosci$mainroad)
knitr::kable(miss_var_summary(agencja_nieruchomosci))| variable | n_miss | pct_miss |
|---|---|---|
| prefarea | 50 | 9.17 |
| price | 0 | 0 |
| area | 0 | 0 |
| bedrooms | 0 | 0 |
| bathrooms | 0 | 0 |
| stories | 0 | 0 |
| mainroad | 0 | 0 |
| guestroom | 0 | 0 |
| basement | 0 | 0 |
| hotwaterheating | 0 | 0 |
| airconditioning | 0 | 0 |
| parking | 0 | 0 |
| furnishingstatus | 0 | 0 |
Dane w ostatniej brakującej kolumnie uzupełniamy metodą
mice czyli Multivariate Imputation by Chained Equations -
wielowymiarowe wypełnianie przez równania łańcuchowe. Jako że
uzupełniamy zmienną binarną, korzystamy z metody
logreg.
imputed_data <- mice(agencja_nieruchomosci, method = "logreg", m = 5, printFlag = FALSE)
completed_data <- complete(imputed_data, 1)
agencja_nieruchomosci <- completed_dataZobaczmy jak wygląda pierwsze 10 wierszy naszego zbioru danych po imputacji:
| price | area | bedrooms | bathrooms | stories | mainroad | guestroom | basement | hotwaterheating | airconditioning | parking | prefarea | furnishingstatus |
|---|---|---|---|---|---|---|---|---|---|---|---|---|
| 7910000 | 7420 | 4 | 2 | 3 | 1 | 0 | 0 | 0 | 1 | 2 | 1 | 2 |
| 12250000 | 8960 | 4 | 4 | 4 | 1 | 0 | 0 | 0 | 1 | 3 | 0 | 2 |
| 12250000 | 9960 | 3 | 2 | 2 | 1 | 0 | 1 | 0 | 0 | 2 | 1 | 1 |
| 12215000 | 7500 | 4 | 2 | 2 | 1 | 0 | 1 | 0 | 1 | 3 | 1 | 2 |
| 11410000 | 7420 | 4 | 1 | 2 | 1 | 1 | 1 | 0 | 1 | 2 | 0 | 2 |
| 10850000 | 7500 | 3 | 3 | 1 | 1 | 0 | 1 | 0 | 1 | 2 | 1 | 1 |
| 10150000 | 8580 | 4 | 3 | 4 | 1 | 0 | 0 | 0 | 1 | 2 | 1 | 1 |
| 10150000 | 16200 | 5 | 3 | 2 | 1 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
| 9870000 | 8100 | 4 | 1 | 2 | 1 | 1 | 1 | 0 | 1 | 2 | 1 | 2 |
| 9800000 | 5750 | 3 | 2 | 4 | 1 | 1 | 0 | 0 | 1 | 1 | 1 | 0 |
completed_data$price <- as.numeric(completed_data$price)
completed_data <- completed_data %>%
mutate(
price_z = (price - mean(price, na.rm = TRUE)) / sd(price, na.rm = TRUE),
price_minmax = scales::rescale(price, to = c(0, 1))
)
boxplot(completed_data$price_z, completed_data$price_minmax,
names = c("Z-score", "Min-max"), main = "Porównanie normalizacji danych")skewness_results <- sapply(completed_data, function(x) if (is.numeric(x)) e1071::skewness(x, na.rm = TRUE) else NA)
knitr::kable(skewness_results)| x | |
|---|---|
| price | 1.2284480 |
| area | 1.3139246 |
| bedrooms | 0.4929587 |
| bathrooms | 1.5805260 |
| stories | 1.0761391 |
| mainroad | -2.2903089 |
| guestroom | 1.6791359 |
| basement | 0.6251337 |
| hotwaterheating | 4.3294938 |
| airconditioning | 0.7913726 |
| parking | 0.8374328 |
| prefarea | 1.2236374 |
| furnishingstatus | 0.1170196 |
| price_z | 1.2284480 |
| price_minmax | 1.2284480 |
| variables | outliers_cnt | outliers_ratio | outliers_mean | with_mean | without_mean |
|---|---|---|---|---|---|
| price | 13 | 2.3853211 | 1.053123e+07 | 4.721686e+06 | 4.579723e+06 |
| area | 12 | 2.2018349 | 1.269325e+04 | 5.150541e+03 | 4.980724e+03 |
| bedrooms | 12 | 2.2018349 | 5.166667e+00 | 2.965138e+00 | 2.915572e+00 |
| bathrooms | 1 | 0.1834862 | 4.000000e+00 | 1.286239e+00 | 1.281250e+00 |
| stories | 41 | 7.5229358 | 4.000000e+00 | 1.805505e+00 | 1.626984e+00 |
| mainroad | 67 | 12.2935780 | 0.000000e+00 | 8.770642e-01 | 1.000000e+00 |
| guestroom | 97 | 17.7981651 | 1.000000e+00 | 1.779817e-01 | 0.000000e+00 |
| basement | 0 | 0.0000000 | NaN | 3.504587e-01 | 3.504587e-01 |
| hotwaterheating | 25 | 4.5871560 | 1.000000e+00 | 4.587160e-02 | 0.000000e+00 |
| airconditioning | 0 | 0.0000000 | NaN | 3.155963e-01 | 3.155963e-01 |
| parking | 12 | 2.2018349 | 3.000000e+00 | 6.935780e-01 | 6.416510e-01 |
| prefarea | 130 | 23.8532110 | 1.000000e+00 | 2.385321e-01 | 0.000000e+00 |
| furnishingstatus | 0 | 0.0000000 | NaN | 9.302752e-01 | 9.302752e-01 |
| price_z | 13 | 2.3853211 | 3.269696e+00 | 0.000000e+00 | -7.989860e-02 |
| price_minmax | 13 | 2.3853211 | 8.363077e-01 | 2.830177e-01 | 2.694975e-01 |
dane <- (agencja_nieruchomosci)
Q1 <- quantile(dane$stories, 0.25, na.rm = TRUE)
Q3 <- quantile(dane$stories, 0.75, na.rm = TRUE)
IQR <- Q3 - Q1
lower_bound <- Q1 - 1.5 * IQR
upper_bound <- Q3 + 1.5 * IQR
outliers <- dane$stories[dane$stories < lower_bound | dane$stories > upper_bound]
boxplot(dane$stories, main = "Wartości odstające w zmiennej 'stories'",
col = "lightblue", horizontal = TRUE)dane_cleaned <- dane %>%
filter(stories >= lower_bound, stories <= upper_bound)
boxplot(dane_cleaned$stories, main = "wykres pudełkowy dla zmiennej 'stories' bez wartości odstających",
col = "lightblue", horizontal = TRUE)Wykres pudełkowy dla zmiennej “stories” bez wartości odstających - został nałożony filtr nieuwzględniający wartości odstających.
Po oczyszczeniu zbioru danych przeprowadzono eksploracyjną analizę danych (EDA) w celu zrozumienia ich rozkładu oraz zależności pomiędzy zmiennymi. Wykorzystano histogramy, wykresy punktowe oraz wykresy skrzynkowe do przedstawienia kluczowych zależności.
Wyniki analizy wskazują na silny związek między ceną a powierzchnią domu. Dodatkowo, liczba miejsc parkingowych oraz liczba pięter również wydają się mieć wpływ na wartość nieruchomości. W szczególności, nieruchomości posiadające 3 sypialnie wykazują większą liczbę brakujących wartości w kolumnie cena, co może wskazywać na specyficzne strategie rynkowe.
completed_data$price <- completed_data$price / 1000
completed_data$area <- completed_data$area * 0.092903
ggplot(completed_data, aes(x=price))+
geom_histogram(bins = 10)+
labs(title="Ilośc domów z przedziałami cenowymi", x="Price", y="Ilość domów")+
theme_bw() +
facet_grid(~parking)completed_data$bedfac <- as.factor(completed_data$bedrooms)
ggplot(completed_data, aes(x = area, y = price, color = bedfac)) +
geom_point(alpha = 0.7, size = 3) +
scale_color_brewer(palette = "Set1") +
theme_ipsum() +
facet_grid(~mainroad) +
labs(
x = "Wielkość domu",
y = "Cena",
color = "Liczba sypialni")+
theme( text = element_text(family = "sans"))ggplot(completed_data, aes(x = bedrooms, y = price, color = bedfac)) +
geom_point(alpha = 0.7, size = 3) +
scale_color_brewer(palette = "Set1") +
theme_ipsum() +
theme(
text = element_text(family = "sans") ## Użyj czcionki sans
) +
facet_grid(~prefarea) +
labs(
x = "ilość sypialni",
y = "Cena",
color = "Liczba sypialni"
)completed_data$prefarea1 <- as.factor(completed_data$prefarea) ## Zamiana na factor
p <- ggplot(completed_data, aes(x = price, y = area)) +
geom_point(aes(color = prefarea1)) +
geom_smooth(method = "lm", se = TRUE)+
xlab("Cena")+
ylab("Powierzchnia domu")+
scale_color_discrete(name = "Preferowane miejsce")+
ggtitle("Metraż oraz cena domu")+
theme_light()
plotly::ggplotly(p)Poniższy wykres przedstawia rozkład powierzchni domu w zależności od liczby pięter i ceny. Dzięki niej możemy zaobserwować, iż najwięcej mieszkań posiada 1 bądź 2 piętra. Dodatkowo możemy przeanalizować jak kształtuje się cena wśród takich mieszkań. Interpretując wykres dostrzegamy, iż im mniejsza powierzchnia oraz liczba pięter tym niższa cena.
completed_data$pietra <- as.factor(completed_data$stories)
ggplot(completed_data, aes(x = stories, y = area)) +
geom_boxplot(aes(group = stories), outlier.shape = NA) +
geom_jitter(aes(color = price), width = 0.2, alpha = 0.7) +
scale_color_gradient(name = "Cena", low = "lightblue", high = "navyblue") +
xlab("Liczba pięter") +
ylab("Powierzchnia domu") +
ggtitle("Rozkład powierzchni domu w zależności od liczby pięter i ceny") +
theme_minimal()By móc się dokładnie przyjrzeć jak prezentują się wartości dt. powierzchni w podziale na liczbę pokoi została stworzona tabela. Najwyższy średni metraż posiadają mieszkania 4 pokojowe, co nie powinno dziwić. Natomiast najniższy średni metraż oraz medianę posiadają mieszkania 2 pokojowe.
summary_table <- completed_data %>%
group_by(pietra) %>%
summarise(
Srednia_powierzchnia = mean(area, na.rm = TRUE),
Mediana_powierzchnia = median(area, na.rm = TRUE),
Std_powierzchnia = sd(area, na.rm = TRUE),
Srednia_cena = mean(price, na.rm = TRUE),
Mediana_cena = median(price, na.rm = TRUE),
Std_cena = sd(price, na.rm = TRUE),
Liczba_obserwacji = n()
)
knitr::kable(summary_table)| pietra | Srednia_powierzchnia | Mediana_powierzchnia | Std_powierzchnia | Srednia_cena | Mediana_cena | Std_cena | Liczba_obserwacji |
|---|---|---|---|---|---|---|---|
| 1 | 491.4405 | 418.0635 | 211.4452 | 4155.658 | 3815 | 1367.579 | 227 |
| 2 | 441.9345 | 386.9410 | 197.1835 | 4723.147 | 4200 | 1841.284 | 238 |
| 3 | 493.9319 | 510.9665 | 183.7000 | 5451.385 | 5810 | 1289.063 | 39 |
| 4 | 604.4428 | 557.4180 | 111.2456 | 7152.962 | 7245 | 1518.262 | 41 |
mean_price <- mean(completed_data$price)
sd_price <- sd(completed_data$price)
completed_data$z_score <- (completed_data$price - mean_price) / sd_price
completed_data$price_category <- cut(
completed_data$z_score,
breaks = c(-Inf, -1, 1, Inf),
labels = c("tanie", "średnie", "drogie")
)
category_counts <- completed_data %>%
group_by(price_category) %>%
summarise(count = n())Wykres przedstawia ceny nieruchomości, które należą do agencji nieruchomości. Poprzez stworzenie 3 znaczników - tani, średni, drogi możemy przeanalizować w jaki sposób plasują się ceny z bazy danych.
ggplot(category_counts) +
aes(
x0 = 0, y0 = 0,
r0 = 0, r = 1,
amount = count,
fill = price_category
) +
geom_arc_bar(stat = "pie") +
coord_fixed()etykiety<-c("1750-2750 kPLN","2750-3750 kPLN","3750-4750 kPLN","4750-5750 kPLN","5750-6750 kPLN","6750-7750 kPLN","7750-8750 kPLN","9750-10750 kPLN","10750-11750 kPLN","11750-12250 kPLN")
limits<-cut(completed_data$price,seq(1750,12250,by=1000),labels=etykiety)
tabela1<-freq(limits)
knitr::kable(tabela1)| n | % | val% | |
|---|---|---|---|
| 1750-2750 kPLN | 35 | 6.4 | 6.5 |
| 2750-3750 kPLN | 145 | 26.6 | 26.9 |
| 3750-4750 kPLN | 146 | 26.8 | 27.1 |
| 4750-5750 kPLN | 87 | 16.0 | 16.1 |
| 5750-6750 kPLN | 66 | 12.1 | 12.2 |
| 6750-7750 kPLN | 24 | 4.4 | 4.5 |
| 7750-8750 kPLN | 21 | 3.9 | 3.9 |
| 9750-10750 kPLN | 8 | 1.5 | 1.5 |
| 10750-11750 kPLN | 5 | 0.9 | 0.9 |
| 11750-12250 kPLN | 2 | 0.4 | 0.4 |
| NA | 6 | 1.1 | NA |
Poniższy wykres kolumnowy przedstawia rozkład cen mieszkań z podziałem co 1000. Można wywnioskować, iż tak jak na wykresie kołowym, większość mieszkań jest blisko średniej ceny rynkowej. Tańsze mieszkania oraz droższe są w zdecydowanej mniejszości. Dodatkowo zaznaczono liniami jak kształtują się ceny mieszkań w podziale na ilość sypialni.
hist(completed_data$price, breaks="FD", col="green", probability = TRUE,
main="Ceny nieruchomości")
lines(density(completed_data$price[completed_data$bedrooms == 1]), col=2)
lines(density(completed_data$price[completed_data$bedrooms == 2]), col=3)
lines(density(completed_data$price[completed_data$bedrooms == 3]), col=4)
legend("topright",
legend=c("Jedna sypialnia", "Dwie sypialnie", "Trzy sypialnie"),
col=c(2, 3, 4),
lty=1,
horiz=FALSE,
box.lty=0,
cex=0.8)completed_data %>%
select(price, bedfac) %>%
tbl_summary(
by=bedfac,
type = all_continuous() ~ "continuous2",
statistic = all_continuous() ~ c(
"{N_nonmiss}","{mean}","{sd}",
"{median} ({p25}, {p75})",
"{min}, {max}"),
missing = "no",
label = price ~ "Cena") %>%
modify_header(label ~ "**Zmienna**") %>%
modify_caption("**Rozkład cen wg liczby pokoi**") %>%
bold_labels() %>%
add_p(pvalue_fun = ~ style_pvalue(.x, digits = 2))| Zmienna | 1 N = 2 |
2 N = 136 |
3 N = 300 |
4 N = 95 |
5 N = 10 |
6 N = 2 |
p-value |
|---|---|---|---|---|---|---|---|
| Cena | |||||||
| N Non-missing | 2 | 136 | 300 | 95 | 10 | 2 | |
| Mean | 2,713 | 3,647 | 4,919 | 5,551 | 5,925 | 4,792 | |
| SD | 619 | 990 | 1,678 | 2,147 | 2,339 | 1,826 | |
| Median (Q1, Q3) | 2,713 (2,275, 3,150) | 3,500 (2,951, 4,200) | 4,550 (3,640, 5,950) | 5,250 (4,060, 6,300) | 5,583 (3,850, 8,120) | 4,792 (3,500, 6,083) | |
| Min, Max | 2,275, 3,150 | 1,750, 7,070 | 1,750, 12,250 | 2,100, 12,250 | 3,010, 10,150 | 3,500, 6,083 |
Mieszkania 5-pokojowe mają najwyższą średnią cenę spośród wszystkich typów mieszkań icechują się również najwyższym odchyleniem standardowym, co oznacza największe wahania cen w tej kategorii. Mieszkania 1-pokojowe mają najniższą medianę ceny, co sugeruje, że to w tej grupie najczęściej znajdziemy najtańsze nieruchomości. Najwyższe ceny maksymalne występują jednak w mieszkaniach 3- i 4-pokojowych.
W tej sekcji przeprowadzono analizę statystyczną, mającą na celu wyciągnięcie wniosków na podstawie dostępnych danych. Wykorzystano testy statystyczne oraz modele predykcyjne do oceny istotności zależności między zmiennymi. Przeanalizowano m.in. regresję liniową do przewidywania cen nieruchomości oraz testy istotności statystycznej w celu oceny wpływu poszczególnych cech na wartość nieruchomości.
Wyniki analizy wskazują, że zmienne takie jak powierzchnia mieszkania, liczba pokoi oraz lokalizacja mają istotny wpływ na cenę nieruchomości. Regresja liniowa wykazała wysoką korelację między powierzchnią a ceną, natomiast testy statystyczne potwierdziły istotność wpływu lokalizacji na końcową wartość nieruchomości.
Podstawowe statystyki opisowe cen nieruchomości jeżeli znajdują się w preferowanym obszarze (tak/nie - 1/0). Dla obu przypadków są widoczne obserwacje odstające dla zmiennej price. Mediana cen nieruchomości dla tych znajdujących się w preferowanej okolicy jest większa. W bazie więcej jest nieruchomości, które nie znajdują sie w preferowanej okolicy. W przypadku nieruchomości nieznajdujących się w preferowanym obszarze, pierwsza część obserwacji ma większe zagęszczenie przy niskiej cenie 2 - 4 tys., a ceny nad medianą mają większą rozpiętość - od około 4 do 12 tys. Dla nieruchomości w preferowanym obszarze wiolina jest najszersza w okolicach mediany - najwięcej obserwacji kiedy cena jest bliska wartości środkowej - około 6 tys.
data(completed_data)
completed_data %>%
filter(prefarea %in% c(0, 1))%>%
ggbetweenstats(
y=price,
x=prefarea
)Jaki procent nieruchomości o powierzchni z danego przedziału posiada posiada wskazaną liczbę łazienek. Nawet w domach o największej powierzni jest tylko jedna łazienka.
completed_data$area_category <- cut(
completed_data$area,
breaks = c(0, 2000, 5000, 10000, 16200),
labels = c("0-2000", "2001-5000", "5001-10000", "10001-1620"),
right = TRUE
)
completed_data %>%
filter(bathrooms %in% c(1, 2 ,3, 4))%>%
ggpiestats(
y=area_category,
x=bathrooms
)Jaki procent nieruchomości o powierzchni z danego przedziału posiada posiada wskazaną liczbę łazienek. Nawet w domach o największej powierzni dominuje tylko jedna łazienka. 4 łazienki ma jedynie jedna nieruchomość w zbiorze danych, na wykresie jest nawet niewidoczna - występuje w nieruchomość z przedziału 5001 - 10 000. Co ciekawe, większy procent nieruchomości z dwiema łazienkami jest dla nieruchomości z przedziału 5001 - 10 000, niż z 10 001 - 16 200.
df_filtered <- completed_data %>% select(-c(price_z, price_minmax, z_score))
numeric_cols <- sapply(df_filtered, is.numeric)
cor_matrix <- cor(df_filtered[, numeric_cols], use = "complete.obs")
corrplot(cor_matrix, method = "color", type = "upper", tl.col = "black", tl.cex = 0.8)Ten wykres przedstawia macierz korelacji zmiennych numerycznych w zbiorze danych dotyczącym nieruchomości. Cena (price) wykazuje silną pozytywną korelację z powierzchnią (area), liczbą sypialni (bedrooms) i liczbą łazienek (bathrooms). Zmienna hotwaterheating nie ma bardzo niską korelację z pozostałymi zmiennymi. Co ciekawe zmienne Stories i basement mają ujemną korelację, oznacza to że im więcej pięter w budynku, bardziej prawdopodobne że nie ma mieszkanie nie ma piwnicy. Podobna zależność jest między ogrzewaniem ciepłą wodą (hotwaterheating) oraz dostępnością klimatyzacji (airconditioning).
Raport przedstawia kompleksową analizę danych dotyczących nieruchomości, koncentrując się na brakujących wartościach, czyszczeniu danych oraz metodach imputacji. Dane zostały poddane weryfikacji pod kątem błędów logicznych i spójności, a następnie przekształcone w celu ułatwienia dalszej analizy. Kluczowe wnioski:
Podjęte kroki znacząco poprawiły jakość danych i umożliwiły bardziej rzetelną analizę rynku nieruchomości. Zastosowane metody przetwarzania i imputacji danych pozwalają na dalsze wykorzystanie zbioru do prognozowania cen oraz analizy trendów rynkowych. W niniejszyn raporcie skupiono się na technicznych aspektach analizy danych, kolejnym krokiem mogłaby być bardziej zaawansowana analiza statystyczna, np. modelowanie regresyjne czy klasyfikacja, co pozwoliłoby na jeszcze głębsze zrozumienie rynku nieruchomości. Wturnym i mniej poruszonym zagadnieniem była sama formalna analiza opisowa danych.